/*
 * GridButtonFactory.java
 *
 * Created on 2007年9月28日, 下午8:54
 *
 * 
 * 
 */

package cn.tearcry.jsudoku;
import java.util.*;
import javax.swing.*;
import java.util.Random;
import java.awt.*;
import java.awt.event.*;
import java.io.*;

/**
 * 代理，提供一些游戏的信息以及操作接口
 * @author 马建乐
 */
public class GridButtonFactory implements Serializable {
    /** 数独格子 */
    private GridButton[][] btnGrid;
    /** 每个格子响应的动作　*/
    private SudokuAction[][] btnAction;
    /** The Main Frame */
    private MainFrame frame;
    /** ９个九宫格 */
    private JPanel[] grid;
    /** 放置上面面板的容器 */
    private JPanel gridPanel;
    /** 初始数独数据 */
   private int[][] puzzleData=new int[9][9];
     
   /** 解题逻辑对象 */
   private SolvePuzzle puzzle;
   /** 正确解答的格局信息 */
   private int[][] answer;   
   /** 当前游戏用户走的步数(即正确步数）*/
   private int currentStep=0;
   /** 初始空格数 */
   private int blankNumber=81;
   /** 当前空格数 */
   private int currentBlankNumber;   
   /** 游戏正确解答的的建议步骤 */
   private ArrayList currentStepList=new ArrayList();
   
   private javax.swing.Timer timer;
   /** 撤消管理器 */
   private UndoManager manager;
   /** 是否显示候选数 */
   private boolean displayAvailableNum;
  /** 当前游戏状态 */
   private  int  gameState;
   /** 当前游戏状态常量 进行中*/
   public static final int PLAYING=1;
   /** 当前游戏状态常量 结束*/
   public static final int END=2;
   private SaveAndLoadGame salg;
   private int[][] currentPuzzleData=new int[9][9];
   private boolean SAVE=false;
   private boolean blockWrongMove=true;
   private int wrongMoveCount=0;
   private int hintCount=0;
   private int flashCount=0;
   private static Color oriColor;
    /**
     * 创建一个新的GridButtonFactory代理对象
     * @param frame 执行操作的界面
     * @param gridPanel 按钮要添加的面板
     */
    public GridButtonFactory (MainFrame frame,JPanel gridPanel) {
        this.frame=frame;
        this.gridPanel=gridPanel;
        initComponent();
        manager=new UndoManager(this,frame.getUndoComponents(),frame.getRedoComponents());
        oriColor=frame.gameWrongInfoLabel().getForeground();
    }
    
    /*
     * 初始化各个组件
     **/
    private void initComponent() {
        btnGrid=new GridButton[9][9];
        btnAction=new SudokuAction[9][9];
        grid=new JPanel[9];
    }
    
     public void createButton() {
       
      //  Random rand=new Random();      
   //     data=CreateSudoku.getSudokuPuzzle();
        
        //下面开始各个按钮
        for( int i=0;i<btnGrid.length;i++) {
            for(int j=0;j<btnGrid[i].length;j++) {
                btnGrid[i][j]=new GridButton(i,j);
                btnAction[i][j]=new SudokuAction(btnGrid[i][j],btnGrid,this);
               btnGrid[i][j].addButtonAction(btnAction[i][j]);
              //第一次载入时禁用按钮和动作
                btnGrid[i][j].setEnabled(false);
            }
            
        }

        for(int i=0;i<grid.length;i++) {
            grid[i]=new JPanel(new GridLayout(3,3,3,3));
            grid[i].setBorder(javax.swing.BorderFactory.createLineBorder(Color.BLACK, 2)); 
            gridPanel.add(grid[i]);
        }
        
        for(int i=0;i<grid.length;i++) {
            int m=(int)(i/3);
            int n=i%3;
            for(int j=m*3;j<(m+1)*3;j++) {
                for(int k=n*3;k<(n+1)*3;k++)
                    grid[i].add (btnGrid[j][k]);
            }
        }
    }
    
     private void initProgressBar() {
            JProgressBar bar=frame.getGameProgressBar();
            bar.setValue(0);
            bar.setMaximum(blankNumber);
            bar.setMinimum(0);
     }
     
     
     /**
      * 开始新一局游戏 初始化相关数据
      */     
     public void startTheGame() {
         
          currentStep=0;
          currentBlankNumber=81;
        //如果正在游戏中 用户要求退出 应给予确认提示
         if(getGameState()==PLAYING) {
            int choice=JOptionPane.showConfirmDialog(frame,"您确认要离开当前游戏吗","jSudoku",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE);
            if (choice==JOptionPane.NO_OPTION)
                return;               
        }
            puzzleData=new CreateSudoku().create();
              readGameData(puzzleData);             
            initProgressBar();
          //游戏状态置为进行中  
            setGameState(PLAYING);
    }
     
     
     
     
     private void readGameData(int[][] gamePuzzle) {
        
         initTheGameData();
          //恢复游戏信息显示
        frame.gameInfoLabel().setText("");
        frame.getDisplayCandidatesItem().setEnabled(true);
        frame.getDisplayCandidatesItem().setSelected(false);
        displayAvailableNum=false;
         for( int i=0;i<btnGrid.length;i++) 
            for(int j=0;j<btnGrid[i].length;j++) 
            btnGrid[i][j].clear();
        //游戏局面
        puzzle=new SolvePuzzle(gamePuzzle);
        //取得正确答案
        answer=puzzle.getAnswer();
        //取得解题步骤列表
        currentStepList=puzzle.getStepList();        
        currentBlankNumber=blankNumber=currentStepList.size();
        //运行次数加1 只在第一次运行时初始化
        //runTimes++;
       
        for( int i=0;i<btnGrid.length;i++) 
            for(int j=0;j<btnGrid[i].length;j++) {
                //按钮动作打开
                btnGrid[i][j].setEnabled(true);
                //初始化开关打开 开始初始化
                btnGrid[i][j].startInit(true);
                //置入题目数据
                btnGrid[i][j].setNum(gamePuzzle[i][j]); 
                //初始化开关关闭 初始化完毕
                btnGrid[i][j].startInit(false);
                
                //每次新开局 保证显示候选数为关
                if(!(btnGrid[i][j].isPuzzleFill()||btnGrid[i][j].isUserHasFill())) 
                   btnAction[i][j].setDisplayAvilableNum(false);
                //调试用　发布时删去
               // btnGrid[i][j].setToolTipText("<html>userHasFill   "+btnGrid[i][j].isUserHasFill()+"<br>puzzleFill   "+btnGrid[i][j].isPuzzleFill());
            }
        frame.gameInfoLabel().setText(currentStep+"格完成，还有"+currentBlankNumber+"格");
        frame.updateComponentEnabled(true);
        frame.updateRestartComponent(true);
     }
    
     private void readGameData(int[][] gamePuzzle,HashMap map) {
           
      for( int i=0;i<btnGrid.length;i++) 
            for(int j=0;j<btnGrid[i].length;j++) 
            btnGrid[i][j].clear();
         //恢复游戏信息显示
        frame.gameInfoLabel().setText("");
        frame.getDisplayCandidatesItem().setEnabled(false);
         readGameData(gamePuzzle);
         //userFilledList
         if(map==null)
             return;
         Iterator iter=map.entrySet().iterator();
         int location=0,row=0,column=0,num=0;
         while(iter.hasNext()) {
             Map.Entry entry=(Map.Entry)iter.next();
             location=((Integer)entry.getKey()).intValue();
             num=((Integer)entry.getValue()).intValue();
             row=location/9;
             column=location%9;
             btnGrid[row][column].setNum(num);
             int[] info=new int[3];
             info[0]=row;//记录行
             info[1]=column;//记录列
             info[2]=num;            
             goOneStep();
         }
          
         
     }
     
    
    //结束游戏
    private void stopTheGame() {
        for(int i=0;i<btnGrid.length;i++) 
            for(int j=0;j<btnGrid[i].length;j++) {
                btnGrid[i][j].setEnabled(false);
                btnAction[i][j].clearAllColor();
            }
        frame.getDisplayCandidatesItem().setEnabled(false);
        currentStep=0;
        //frame.updateComponentEnabled(false);
    }
    /**
     *  用户进行添加操作 更新游戏相关信息
     */
    public void goOneStep() {
       //步数加1 
       if(currentStep<81) 
            currentStep++;
       frame.gameInfoLabel().setText(currentStep+"格完成，还有"+(--currentBlankNumber)+"格");
       closeDisplayGameInfo();
       //修改当前解格局 产生新的解题步骤
   //    updatePuzzle();
       frame.getGameProgressBar().setValue(currentStep);
       closeDisplayGameInfo();
       SAVE=false;
       if(getCurrentStep()==getBlankNumber()) {
                        JOptionPane.showMessageDialog(frame,"<html>恭喜你完成了本道题目<p align='left'>错误移动:"+wrongMoveCount+
                                "次</p><p align='left'>使用提示:"+hintCount+"次</p><html>",
                                "jSudoku 游戏完成",JOptionPane.INFORMATION_MESSAGE);
                        //将游戏状态设置为结束
                        setGameState(END);
                        closeDisplayGameInfo();
                }
    }
    
    /**
     *  用户回退一步 更新游戏相关信息
     */
    public void backOnStep() {
        //步数减1 
        if(currentStep>0)
            currentStep--;
        frame.gameInfoLabel().setText(currentStep+"格完成，还有"+(++currentBlankNumber)+"格");
      //  updatePuzzle();
       frame.getGameProgressBar().setValue(currentStep);
       closeDisplayGameInfo();
       SAVE=false;
    }
    
    /*
     * 取得当前游戏步数
     * @return int 当前用户正确的步数
     */    
    public int getCurrentStep() {
       return currentStep; 
    }
    
    /**
     * 判断当前用户输入是否正确
     * @return boolean 用户输入是否正确
     */
    public boolean isVaild(int num,int row,int column) {
        if(answer[row][column]==num)
            return true;
        return false;
    }
        
    /** 
     * 查询当前按钮在当前状态下的候选数<BR>
     * 数据由AvailableNum类提供
     * @param  button 要查询的按钮
     * @return  供显示的格式化后候选数字符串
     */
    public  String availableNums(GridButton button) {
        ArrayList <Integer> availableNum=new AvailableNum(button,btnGrid).getAvailableNum();
        if(availableNum==null)
            return "";
        //采用HTML字符串显示 粗体 居中 字号为3 颜色可变
        StringBuffer sb=new StringBuffer("<HTML><CENTER><FONT NAME='宋体' SIZE='2' COLOR='#FF0000'>");
        
        Iterator <Integer> iter=availableNum.iterator();/*
        while(iter.hasNext()) {
           if(i%3==1&&i!=1)
               sb.append("<P ALIGN='LEFT'>");
            sb.append(iter.next().intValue()+" ");
            if(i%3==0&&i!=3)
                sb.append("</P>");
            i++;           
        }
        */
        
        char[][] matrix=new char[4][4];
        int x=0,y=0;
        while(iter.hasNext()) {
            int num=iter.next().intValue();
            x=(num-1)/3+1;
            y=(num%3==0)?3:num%3;
            matrix[x][y]=(char)((int)'0'+num);
        }
        
        for(int i=1;i<=3;i++) {
            if(i!=1)
                sb.append("<P ALIGN='LEFT'>");
            for(int j=1;j<=3;j++) {
                sb.append(matrix[i][j]+" ");
            }
            if(i!=1)
                sb.append("</P>");
        }
            
          
        sb.append("</CENTER></HTML>");
        return sb.toString();
    }
    
    /**
     * 显示候选数
     **/
    public void displayCandidates() {
        for(int i=0;i<btnGrid.length;i++) {
            for(int j=0;j<btnGrid[i].length;j++) {
                if(!(btnGrid[i][j].isPuzzleFill()||btnGrid[i][j].isUserHasFill())) {
                    btnAction[i][j].setDisplayAvilableNum(true);
                    btnGrid[i][j].setText(availableNums(btnGrid[i][j]));
                }
            }
        }
        displayAvailableNum=true;
       //关闭错误信息
        closeDisplayGameInfo();
    }
    
    /**
     * 关闭候选数
     **/
    public void displayOriginalNum() {
        for(int i=0;i<btnGrid.length;i++) {
            for(int j=0;j<btnGrid[i].length;j++) {
               if(!(btnGrid[i][j].isPuzzleFill()||btnGrid[i][j].isUserHasFill())) {
                   btnAction[i][j].setDisplayAvilableNum(false);
                    btnGrid[i][j].setNum(0);
                }
            }
        }
       displayAvailableNum=false;
       //关闭错误信息
       closeDisplayGameInfo();
    }
    
    /**
     * 设置游戏状态
     * @param state 要设置的游戏状态
     */
    public void setGameState(int state) {
        if(getGameState()!=PLAYING&&getGameState()!=END)
            return;
        gameState=state;
        if(gameState==END)
            stopTheGame();
            
    }
    
    /** 
     * 获得当前游戏状态
     * @return  当前游戏状态
     */
    public int getGameState() {
        return gameState;
    }
    
     /** 
     * 获得当前空格数     
     * @return  当前空格数
     */
     public int getBlankNumber() {
        return blankNumber;
    }
    
     /**
      * 获得主界面对象
      * @return 主界面对象
      */
     public MainFrame getFrame() {
         return frame;
     }
     
     /**
      * 显示下一步建议提示
      */
     public void displayHint() {
         hintCount++;
         updatePuzzle();
         puzzle=new SolvePuzzle(currentPuzzleData);
         if(puzzle.getStepList().size()==0)
            frame.gameHintLabel().setText("<HTML><FONT COLOR='RED'>错误移动，无法做出下一提示</HTML>");
         currentStepList=puzzle.getStepList();
         //int location=((Integer)currentStepList.get(currentStep)).intValue();
         int location=((Integer)currentStepList.get(0)).intValue();
         int row=location/9;
         int column=location%9;
         btnGrid[row][column].setNum(answer[row][column]);
         if(displayAvailableNum)
             displayCandidates();
         frame.gameHintLabel().setText("提示:"+location/9+"行，"+location%9+"列,填写"+answer[row][column]);
         int info[]={row,column,answer[row][column]};
         //记录复原数据
         manager.record(info);
         goOneStep();
         
         
      }
     
     /**
      * 更新游戏题目格局 动态产生新的解题步骤
      */
     private void updatePuzzle() {
         for(int i=0;i<btnGrid.length;i++)
             for(int j=0;j<btnGrid[i].length;j++)
                 currentPuzzleData[i][j]=btnGrid[i][j].getNum();
     }
     
     
    
      
      public void displayWrongInfo() {
          wrongMoveCount++; 
          frame.gameWrongInfoLabel().setForeground(Color.RED);
          frame.gameWrongInfoLabel().setText("填写错误");
                  
         
      }
  
 
      /**
        * 关闭显示错误信息
        */   
  public void closeDisplayGameInfo() {
     
        frame.gameWrongInfoLabel().setText("");
        frame.gameHintLabel().setText("");
  }
  
  /**
   * 获得当前可操作的按钮对象数据
   * @return GridButton[][] 当前可操作的按钮对象数据
   */
  public GridButton[][] getGridArray() {
        return btnGrid;
  }
  
  /** 
   * 获得当前可用撤消管理器
   * @return UndoManager 当前可用撤消管理器
   */
  public UndoManager getUndoManager() {
      return manager;
  }
  
  /**
   * 获得当前候选数显示状态
   * @boolean 当前候选数显示状态
   */
  public boolean getDisplayCandidates() {
      return displayAvailableNum;
  }
  
  /**
   * 保存当前的游戏
   */
  public void saveGame() {
      Map userFilledMap=new HashMap();
      for(int i=0;i<btnGrid.length;i++) 
          for(int j=0;j<btnGrid[i].length;j++)            
              if(!btnGrid[i][j].isPuzzleFill()&&btnGrid[i][j].isUserHasFill()) 
                  userFilledMap.put(Integer.valueOf(btnGrid[i][j].getRow()*9+btnGrid[i][j].getColumn()),
                          Integer.valueOf(btnGrid[i][j].getNum()));
           salg= new SaveAndLoadGame(frame,puzzleData,userFilledMap,wrongMoveCount,hintCount);
           //提示用户保存的结果
           boolean successed=salg.saveGame();
           JOptionPane.showMessageDialog(frame,"游戏"+(successed?"已经成功":"未能")+"保存","jSudoku 保存游戏",JOptionPane.INFORMATION_MESSAGE);
               
      }
  
  /**
   * 读取游戏
   */
  public void loadGame() {
    if(!SAVE&&getGameState()==PLAYING) {
        int choice=JOptionPane.showConfirmDialog(frame,"你当前进行的游戏还未保存，是否保存","jSudoku",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE);
            if (choice==JOptionPane.YES_OPTION)
                saveGame();
        SAVE=true;
    }
    
        SaveAndLoadGame salg=new SaveAndLoadGame();
        Object[] readData=salg.loadGame();
        if(readData==null)
            return;
        puzzleData=(int[][])readData[0];
        HashMap map=(HashMap)readData[1];
        initProgressBar();
        readGameData(puzzleData,map);
        wrongMoveCount=Integer.parseInt(readData[2].toString());
        hintCount=Integer.parseInt(readData[3].toString());
        frame.getDisplayCandidatesItem().setEnabled(true);
        frame.getDisplayCandidatesItem().setSelected(false);
        setGameState(PLAYING);
  }
  /**
   * 重新开局
   */
  public void restartTheGame() {
      if(puzzleData==null)
          return;
      initProgressBar();
      initTheGameData();
      readGameData(puzzleData);
          //游戏状态置为进行中  
            gameState=PLAYING;
  }
  /*
   * 初始化游戏数据
   */
  private void initTheGameData() {
      currentStep=0;
      wrongMoveCount=0;
      hintCount=0;
      manager.restoreManager();
      frame.gameHintLabel().setText("");
  }
  /**
   * 设置是否阻止错误移动
   * @param boolean 是否阻止错误移动
   */
  public void setBlockWrongMove(boolean block) {
      blockWrongMove=block;
  }
  /**
   * 取得阻止错误移动的设置
   * @return boolean 是否阻止错误移动
   */
  public boolean getBlockWrongMove() {
      return blockWrongMove;
  }
  
  /**
   * 填充所有的空格
   */
  public void fillAll(){
      GridButton button=null;
      for(int i=0;i<btnGrid.length;i++) {
          for(int j=0;j<btnGrid[i].length;j++) {
                button=btnGrid[i][j];
                if(!button.isUserHasFill()&&!button.isPuzzleFill()) {
                    button.setNum(answer[i][j]);
                    hintCount++;
                    goOneStep();
                }
          }
      }
      setGameState(END);
      stopTheGame();
  }

}
